﻿/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * Author: Laurent WOUTERS
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace xowl.gmi.compiler
{
    class Compiler
    {
        private const string uriWorkbench = "http://www.xowl.org/gmi/workbench/";
        private const string uriXsd = "http://www.w3.org/2001/XMLSchema";
        private const string uriCVSSL = "http://www.xowl.org/gmi/model/cvssl";
        private const string uriAst = "http://www.xowl.org/gmi/model/ast";
        private const string uriMeta = "http://www.xowl.org/gmi/model/meta";
        private const string uriCommand = "http://www.xowl.org/gmi/model/command";
        private const string uriLib = "http://www.xowl.org/library/indexer";

        private string workbench;
        private Dictionary<string, ViewResource> views;
        private Dictionary<string, TransformResource> transforms;

        public Compiler(string workbench)
        {
            this.workbench = workbench;
            this.views = new Dictionary<string, ViewResource>();
            this.transforms = new Dictionary<string, TransformResource>();
        }

        public void LoadView(string file)
        {
            ViewResource res = new ViewResource(file);
            if (res.Load())
                this.views.Add(res.Name, res);
        }
        public void LoadTransform(string file)
        {
            TransformResource res = new TransformResource(file);
            if (res.Load())
                this.transforms.Add(res.Name, res);
        }

        public void Compile()
        {
            model.Model model = new model.Model();
            foreach (string name in views.Keys)
            {
                Program.Logger.Info("Compiling view " + name);
                views[name].Definition.BuildDefaultModel(model);
                WriteViewModel(views[name].Definition, name, views[name].GetTarget(".view.owl"));
                if (transforms.ContainsKey(name))
                {
                    Program.Logger.Info("Updating transformation " + name);
                    views[name].Definition.BuildTransformation(transforms[name].Definition);
                }
                else
                {
                    Program.Logger.Info("Creating transformation " + name);
                    transform.Transformation transfo = new transform.Transformation();
                    views[name].Definition.BuildTransformation(transfo);
                    transfo.AddPrefix("xsd", uriXsd);
                    transfo.AddPrefix("cvssl", uriCVSSL);
                    transfo.AddPrefix("ast", uriAst);
                    transfo.AddPrefix("meta", uriMeta);
                    transfo.AddPrefix("command", uriCommand);
                    transfo.AddPrefix("lib", uriLib);
                    transfo.AddPrefix("model", uriWorkbench + workbench);
                    transfo.AddPrefix("view", uriWorkbench + workbench + "/" + name + "/view");
                    transfo.AddPrefix(string.Empty, uriWorkbench + workbench + "/" + name + "/transform");
                    WriteTransform(transfo, name, views[name].GetTarget(".transform"));
                    WriteCMTransform(transfo, name, views[name].GetTarget(".transform.cm.owl"));
                    WriteMVTransform(transfo, name, views[name].GetTarget(".transform.mv.owl"));
                    WriteVVTransform(transfo, name, views[name].GetTarget(".transform.vv.owl"));
                }
            }
            foreach (string name in transforms.Keys)
            {
                Program.Logger.Info("Translating transformation " + name);
                WriteCMTransform(transforms[name].Definition, name, transforms[name].GetTarget(".transform.cm.owl"));
                WriteMVTransform(transforms[name].Definition, name, transforms[name].GetTarget(".transform.mv.owl"));
                WriteVVTransform(transforms[name].Definition, name, transforms[name].GetTarget(".transform.vv.owl"));
            }
            if (views.Count != 0)
                WriteMetaModel(model, workbench, Path.Combine(GetModelDirectory(), workbench + ".owl"));
        }

        private string GetModelDirectory()
        {
            List<List<string>> parts = new List<List<string>>();
            int min = Int32.MaxValue;
            foreach (ViewResource res in views.Values)
            {
                string[] temp = res.Directory.Split(Path.DirectorySeparatorChar);
                if (temp.Length < min)
                    min = temp.Length;
                parts.Add(new List<string>(temp));
            }
            List<string> result = new List<string>();
            for (int i = 0; i != min; i++)
            {
                string value = parts[0][i];
                bool ok = true;
                for (int j = 1; j != parts.Count; j++)
                {
                    if (parts[j][i] != value)
                    {
                        ok = false;
                        break;
                    }
                }
                if (ok)
                    result.Add(value);
                else
                    break;
            }
            if (result.Count == 0)
                return string.Empty;
            string final = result[0];
            if (final.Length == 2 && final[1] == ':')
                final += Path.DirectorySeparatorChar;
            for (int i = 1; i != result.Count; i++)
                final = Path.Combine(final, result[i]);
            return final;
        }

        private void WriteViewModel(view.ViewDefinition view, string name, string file)
        {
            Program.Logger.Info("Output view model at " + file);
            StreamWriter writer = new StreamWriter(file);
            writer.WriteLine("Prefix(xsd:=<" + uriXsd + "#>)");
            writer.WriteLine("Prefix(cvssl:=<" + uriCVSSL + "#>)");
            writer.WriteLine("Prefix(:=<" + uriWorkbench + workbench + "/" + name + "/view#>)");
            writer.WriteLine("Ontology(<" + uriWorkbench + workbench + "/" + name + "/view>");
            view.WriteOWL(writer);
            writer.WriteLine(")");
            writer.Close();
        }

        private void WriteMetaModel(model.Model model, string name, string file)
        {
            Program.Logger.Info("Output metamodel at " + file);
            StreamWriter writer = new StreamWriter(file);
            writer.WriteLine("Prefix(xsd:=<" + uriXsd + "#>)");
            writer.WriteLine("Prefix(:=<" + uriWorkbench + workbench + "#>)");
            writer.WriteLine("Ontology(<" + uriWorkbench + workbench + ">");
            model.WriteOWL(writer);
            writer.WriteLine(")");
            writer.Close();
        }

        private void WriteTransform(transform.Transformation transformation, string name, string file)
        {
            Program.Logger.Info("Output transformation at " + file);
            StreamWriter writer = new StreamWriter(file);
            transformation.Write(writer);
            writer.Close();
        }

        private void WriteCMTransform(transform.Transformation transformation, string name, string file)
        {
            Program.Logger.Info("Output CM part at " + file);
            StreamWriter writer = new StreamWriter(file);
            transformation.WriteCMOWL(writer);
            writer.Close();
        }

        private void WriteMVTransform(transform.Transformation transformation, string name, string file)
        {
            Program.Logger.Info("Output MV part at " + file);
            StreamWriter writer = new StreamWriter(file);
            transformation.WriteMVOWL(writer);
            writer.Close();
        }

        private void WriteVVTransform(transform.Transformation transformation, string name, string file)
        {
            Program.Logger.Info("Output VV part at " + file);
            StreamWriter writer = new StreamWriter(file);
            transformation.WriteVVOWL(writer);
            writer.Close();
        }
    }
}
